#include "image2bin.h"
#include <opencv2/opencv.hpp>
#include <iostream>
#include <filesystem>
#include <queue>
#include <cmath>
#include <fstream>

namespace fs = std::filesystem;

// 全局常量定义
const int whiteBlockWidth = 8;
const int sideSize = 50 * whiteBlockWidth;
const int squareSize = 7 * whiteBlockWidth;
const int blockSize = 3 * whiteBlockWidth;
const int dataBegin = 8 * whiteBlockWidth;
const int dataSize = (sideSize - dataBegin) / whiteBlockWidth;

// 全局变量定义
double rowWidth = 0.0;
double colWidth = 0.0;
double rowoffset = 0.0;
double coloffset = 0.0;
bool error_row[42] = { false };
bool error_col[42] = { false };
std::queue<short> indata;

// 函数定义

/** 获取指定文件夹中的文件数量 */
int getfilenum(const std::string& folderpath) {
    if (!fs::exists(folderpath)) {
        std::cerr << "文件夹不存在: " << folderpath << std::endl;
        return 0;
    }
    int filenum = 0;
    for (const auto& entry : fs::directory_iterator(folderpath)) {
        if (entry.is_regular_file()) {
            filenum++;
        }
    }
    return filenum;
}

/** 获取指定位置的颜色（0:白色, 1:黑色） */
int getcolor(cv::Mat& image, int row, int column) {
    int whitenum = 0;
    cv::Vec3b color;
    int rowStart = static_cast<int>(row + rowoffset);
    int colStart = static_cast<int>(column + coloffset);
    int rowEnd = static_cast<int>(rowStart + rowWidth);
    int colEnd = static_cast<int>(colStart + colWidth);

    if (rowEnd > image.rows || colEnd > image.cols) {
        std::cerr << "getcolor: 访问越界 (row: " << row << ", col: " << column << ")" << std::endl;
        return 1; // 默认返回黑色
    }

    for (int i = rowStart; i < rowEnd; i++) {
        for (int j = colStart; j < colEnd; j++) {
            color = image.at<cv::Vec3b>(i, j);
            if (static_cast<int>(color[0]) + static_cast<int>(color[1]) + static_cast<int>(color[2]) > 382) {
                whitenum++;
            }
        }
    }
    return (whitenum > static_cast<int>(rowWidth * colWidth / 2) ? 0 : 1);
}

/** 获取图像编号 */
int getno(cv::Mat& image) {
    int a[16] = { 0 };
    int no = 0;
    for (int i = 0; i < 16; i++) {
        a[i] = getcolor(image, 0, static_cast<int>((8 + i) * colWidth));
        no = no * 2 + a[i];
    }
    return no;
}

/** 获取数据大小 */
int getsize(cv::Mat& image) {
    int a[16] = { 0 };
    int size = 0;
    for (int i = 0; i < 16; i++) {
        a[i] = getcolor(image, 0, static_cast<int>((26 + i) * colWidth));
        size = size * 2 + a[i];
    }
    return size;
}

/** 获取图像数据 */
void getdata(cv::Mat& image, short data[]) {
    if (image.empty()) {
        std::cerr << "getdata: 图像为空" << std::endl;
        return;
    }
    for (int i = 0; i < dataSize; i++) {
        for (int j = 0; j < dataSize; j++) {
            data[i * dataSize + j] = static_cast<short>(getcolor(image, static_cast<int>((8 + i) * rowWidth), static_cast<int>((8 + j) * colWidth)));
        }
    }
}

/** 获取校验码 */
void getcode(cv::Mat& image, short code[]) {
    if (image.empty()) {
        std::cerr << "getcode: 图像为空" << std::endl;
        return;
    }
    for (int i = 0; i < 12; i++) {
        for (int j = 0; j < 7; j++) {
            code[i * 7 + j] = static_cast<short>(getcolor(image, static_cast<int>((8 + i) * rowWidth), static_cast<int>((0 + j) * colWidth)));
        }
    }
}

/** 数据校验 */
bool check(short data[], short code[]) {
    bool ret = true;
    std::fill(error_row, error_row + 42, false);
    std::fill(error_col, error_col + 42, false);

    for (int row = 0; row < dataSize; row++) {
        int rowRes = 0;
        for (int col = 0; col < dataSize; col++) {
            rowRes ^= data[row * dataSize + col];
        }
        rowRes ^= code[row];
        if (rowRes) {
            error_row[row] = true;
            ret = false;
        }
    }

    for (int col = 0; col < dataSize; col++) {
        int colRes = 0;
        for (int row = 0; row < dataSize; row++) {
            colRes ^= data[col + dataSize * row];
        }
        colRes ^= code[dataSize + col];
        if (colRes) {
            error_col[col] = true;
            ret = false;
        }
    }
    return ret;
}

/** 将 8 位数据转换为字节 */
unsigned char bit2byte() {
    unsigned char ret = 0;
    for (int i = 0; i < 8; i++) {
        ret *= 2;
        if (!indata.empty()) {
            ret += static_cast<unsigned char>(indata.front());
            indata.pop();
        }
    }
    return ret;
}

/** 写入二进制文件 */
void writetobin(const std::string& binpath) {
    std::ofstream file(binpath, std::ios::out | std::ios::binary);
    if (!file.is_open()) {
        std::cerr << "无法创建文件: " << binpath << std::endl;
        return;
    }
    int size = static_cast<int>(std::ceil(indata.size() / 8.0));
    for (int i = 0; i < size; i++) {
        unsigned char data = bit2byte();
        file.write(reinterpret_cast<const char*>(&data), 1);
    }
    file.close();
}

/** 将图像转换为二进制文件 */
void img2bin(const std::string& binpath) {
    std::string folderpath = "Dst_image";
    if (!fs::exists(folderpath)) {
        std::cerr << "文件夹不存在: " << folderpath << std::endl;
        return;
    }
    int filenum = getfilenum(folderpath);
    if (filenum == 0) {
        std::cerr << "文件夹中没有文件: " << folderpath << std::endl;
        return;
    }
    short data[2000] = { 0 };
    short code[100] = { 0 };
    bool has_error = false;

    for (int i = 1; i <= filenum; i++) {
        std::string filepath = folderpath + "\\" + std::to_string(i) + ".png";
        cv::Mat image = cv::imread(filepath);
        if (image.empty()) {
            std::cerr << "无法加载图像: " << filepath << std::endl;
            has_error = true;
            continue;
        }
        std::cout << "处理图像 " << i << ": " << image.rows << "x" << image.cols << std::endl;
        if (image.rows < 54 || image.cols < 54) {
            std::cerr << "图像 " << i << " 尺寸太小: " << image.rows << "x" << image.cols << std::endl;
            has_error = true;
            continue;
        }
        rowWidth = image.rows * 1.0 / 54;
        colWidth = image.cols * 1.0 / 54;
        rowoffset = 2 * rowWidth;
        coloffset = 2 * colWidth;

        getdata(image, data);
        getcode(image, code);
        if (!check(data, code)) {
            std::cout << "图像 " << i << " 数据校验失败" << std::endl;
            has_error = true;
        }
        int size = getsize(image);
        if (size > 2000) {
            std::cerr << "图像 " << i << " 数据大小 " << size << " 超过数组限制" << std::endl;
            has_error = true;
            continue;
        }
        for (int j = 0; j < size; j++) {
            indata.push(data[j]);
        }
    }
    if (!has_error) {
        writetobin(binpath);
        std::cout << "二进制文件生成成功！" << std::endl; // 单行，带分号
    }
}